還記得昨天我們學會了 Dependencies 編排微服務嗎?現在你的應用已經能夠優雅地處理服務間的依賴關係了。
但是,作為一個經驗豐富的開發者,你一定遇過這樣的場景:
"咦?這個功能在開發環境運行得好好的,怎麼到了測試環境就出問題了?"
"我想在本機測試一下運營環境的配置,但又不想影響正在開發的版本..."
"能不能讓同一套程式碼,在不同環境下使用不同的設定?"
"或者是嘗試佈署成高可用架構,或者有包含監控功能的系統架構來進行系統行為的測試。"
就像一個變色龍 🦎 能夠根據環境改變自己的顏色一樣,DevSpace Profiles 就是讓你的應用能夠在不同環境間自由切換的魔法師!
✅ 理解 Profiles 四大機制 - 一套配置,多種環境的魔法
✅ 掌握 Activation 自動激活 - 智慧判斷環境自動切換
✅ 學會 Patches/Merge/Replace - 精準修改配置的三大絕技
✅ 實戰環境切換 - 本機輕鬆切換到其他環境測試
傳統做法的痛點
想像你是一家連鎖餐廳的主廚 👨🍳,你有一道招牌菜,但在不同分店需要做些調整:
台北店:口味偏清淡,不加辣椒
台中店:口味適中,微辣
高雄店:口味偏重,要加很多辣椒
傳統做法是什麼?寫三份不同的食譜!但這樣會有什麼問題?
看到了嗎?左邊傳統做法就像是為每家分店都寫一份完整的食譜,90% 的內容都是重複的!
而 DevSpace Profiles 就是圖片右邊,像是寫一份基礎食譜,然後為每家分店只記錄差異:
DevSpace Profiles 提供四種強大的機制,讓你能夠靈活地管理不同環境的配置:
自動根據條件啟用 Profile
vars:
ENV:
default: development
profiles:
- name: production
activation:
- vars:
ENV: production
patches:
- op: replace
path: images.backend.image
value: john/prodbackend
在這個例子中:
當 DevSpace 變數 ENV 的值為 production 時,production profile 會被啟用
可以使用 --var ENV=production
來覆蓋預設值。
使用正則表達式
當環境變數 ENV 匹配正則表達式 prod-\d+
時,profile 會被啟用
profiles:
- name: production
activation:
- env:
ENV: "prod-\\d+"
patches:
- op: replace
path: images.backend.image
value: john/prodbackend
組合使用 vars 和 env,全部匹配(AND
邏輯)
profiles:
- name: production
activation:
- env:
CI: "true"
vars:
ENV: "development"
patches:
- op: replace
path: images.backend.image
value: john/devbackend
在同一個 activation 中指定多個條件時,所有條件都必須匹配才能啟用 profile。
任一匹配(OR
邏輯)
profiles:
- name: production
activation:
- env:
CI: "true"
- vars:
ENV: "development"
patches:
- op: replace
path: images.backend.image
value: john/devbackend
使用多個 activation 項目時,任何一個匹配就會啟用 profile。
注意看這裡的 activation 是陣列
概念比喻:就像醫生做外科手術 🏥,需要在特定位置進行精準的操作。
profiles:
- name: production
patches:
- op: <operation> # 操作類型
path: <json_path> # 目標路径
value: <new_value> # 新值(某些操作不需要)
🎯 add - 新增操作
# 基礎配置
deployments:
backend:
image: myapp:latest
replicas: 1
# Profile 使用 add 操作
profiles:
- name: production
patches:
- op: add
path: deployments.backend.env
value:
- name: NODE_ENV
value: production
- op: add
path: deployments.backend.resources
value:
limits:
memory: "1Gi"
cpu: "500m"
透過執行devspace print -p production
來看結果結果:
version: v2beta1
name: demo
deployments:
backend:
helm:
chart:
name: ./chart
values:
env: # 新增
- name: NODE_ENV
value: production
image:
repository: myapp
tag: latest
replicaCount: 1
resources: # 新增
limits:
cpu: 500m
memory: 1Gi
🔄 replace - 替換操作
# 基礎配置
version: v2beta1
name: demo
deployments:
backend:
helm:
chart:
name: ./chart
values:
image:
repository: myapp
tag: latest
replicaCount: 1
profiles:
- name: production
patches:
- op: replace
path: deployments.backend.helm.values.image.tag
value: prod
- op: replace
path: deployments.backend.helm.values.replicaCount
value: 3
透過執行devspace print -p production
來看結果結果:
version: v2beta1
name: demo
deployments:
backend:
helm:
chart:
name: ./chart
values:
image:
repository: myapp
tag: prod # 被替換
replicaCount: 3 # 被替換
❌ remove - 刪除操作
version: v2beta1
name: demo
images:
backend:
image: john/devbackend
backend-debugger:
image: john/debugger
# 基礎配置
deployments:
backend:
helm:
values:
containers:
- image: john/devbackend
- image: john/debugger
# Profile 使用 remove 操作
profiles:
- name: production
patches:
- op: remove
path: deployments.backend.helm.values.containers.image=john/debugger
結果
version: v2beta1
name: demo
images:
backend:
image: john/devbackend
backend-debugger:
image: john/debugger
deployments:
backend:
helm:
values:
containers:
- image: john/devbackend
概念比喻:就像調雞尾酒 🍹,將新的配料溫和地融入原有的基酒中,不會破壞原有的味道。
基本特性︰
📊 深度合併:自動處理嵌套結構
🔗 保留原值:只覆蓋指定的部分
🧠 智慧判斷:物件合併,陣列替換
🌟 物件合併
version: v2beta1
name: demo
# 基礎配置 - 使用 kubectl
deployments:
backend:
kubectl:
manifests:
- deployment.yaml
- service.yaml
# Profile 使用 merge
profiles:
- name: production
merge:
deployments:
backend:
kubectl:
manifests:
- deployment-prod.yaml # 營運環境專用的 manifest
- service.yaml
透過執行devspace print -p production
來看結果結果:
version: v2beta1
name: demo
deployments:
backend:
kubectl:
manifests:
- deployment-prod.yaml
- service.yaml
📋 陣列完整替換
version: v2beta1
name: demo
images:
backend:
image: john/devbackend
backend-debugger:
image: john/debugger
deployments:
backend:
helm:
values:
containers:
- image: john/devbackend
- image: john/debugger
profiles:
- name: production
merge:
images:
# Change the backend image
backend:
image: john/prodbackend
# Delete the backend-debugger image
backend-debugger: null
# Override deployments
deployments:
backend:
helm:
values:
containers:
- image: john/prodbackend
來看結果
version: v2beta1
name: demo
images:
backend:
image: john/prodbackend
deployments:
backend:
helm:
values:
containers:
- image: john/prodbackend
特點:
🧠 智慧合併:自動處理嵌套結構
🔗 保留原值:只覆蓋指定的部分
📝 語法簡潔:比 Patches 更直觀
### 🔄 4. Replace(完整替換)
完全替換整個配置區塊
```yaml
profiles:
- name: development
replace:
deployments:
backend:
image: myapp:dev
replicas: 1
env:
- name: NODE_ENV
value: development
特點:
🎯 完全控制:替換整個配置區塊
🚀 簡單直接:適合差異較大的環境
⚠️ 注意:會完全覆蓋原有配置
概念比喻:就像重新裝潢房間 🏠,把整個房間清空,然後按照新的設計重新佈置。
基本特性
🎯 完全控制:替換整個配置區塊
🚀 簡單直接:適合差異較大的環境
🏗️ 完整替換部署配置
version: v2beta1
name: demo
images:
backend:
image: john/devbackend
backend-debugger:
image: john/debugger
deployments:
app-backend:
helm:
values:
containers:
- image: john/devbackend
- image: john/debugger
profiles:
- name: production
replace:
images:
backend:
image: john/prodbackend
看結果
version: v2beta1
name: demo
images:
backend:
image: john/prodbackend
deployments:
app-backend:
helm:
values:
containers:
- image: john/devbackend
- image: john/debugger
version: v2beta1
vars:
ENV:
default: development
IMAGE_TAG:
default: latest
REPLICAS:
default: "1"
# 基礎 image 配置
images:
backend:
image: mycompany/backend
dockerfile: ./backend/Dockerfile
tags:
- ${IMAGE_TAG}
frontend:
image: mycompany/frontend
dockerfile: ./frontend/Dockerfile
tags:
- ${IMAGE_TAG}
# 基礎部署配置(開發環境)
deployments:
backend:
helm:
chart:
name: ./charts/backend
values:
image:
repository: mycompany/backend
tag: ${IMAGE_TAG}
replicaCount: ${REPLICAS}
service:
type: ClusterIP
port: 3000
env:
- name: NODE_ENV
value: development
- name: LOG_LEVEL
value: debug
- name: DB_HOST
value: localhost
resources:
requests:
memory: "128Mi"
cpu: "100m"
frontend:
helm:
chart:
name: ./charts/frontend
values:
image:
repository: mycompany/frontend
tag: ${IMAGE_TAG}
replicaCount: 1
service:
type: ClusterIP
port: 80
env:
- name: API_URL
value: http://backend:3000
# 開發工具配置
dev:
backend:
imageSelector: mycompany/backend:${IMAGE_TAG}
ports:
- port: "3000:3000"
sync:
- path: ./backend:/app
excludePaths:
- node_modules/
terminal:
command: ./devspace_start.sh
# 🎭 Profiles 配置
profiles:
- name: production
activation:
- vars:
ENV: production
- env:
CI: "true"
replace:
deployments:
backend:
helm:
chart:
name: ./charts/backend
values:
image:
repository: mycompany/backend
tag: prod-${IMAGE_TAG}
replicaCount: 5 # 高可用性
service:
type: LoadBalancer
port: 3000
env:
- name: NODE_ENV
value: production
- name: LOG_LEVEL
value: error
- name: DB_HOST
value: prod-db-cluster.example.com
- name: REDIS_URL
value: redis://prod-redis-cluster:6379
resources:
requests:
memory: "1Gi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "1000m"
frontend:
helm:
chart:
name: ./charts/frontend
values:
image:
repository: mycompany/frontend
tag: prod-${IMAGE_TAG}
replicaCount: 3
service:
type: LoadBalancer
port: 80
env:
- name: API_URL
value: https://api.example.com
merge:
deployments:
backend:
helm:
values:
labels:
environment: production
monitoring: enabled
team: backend
env:
- name: NODE_ENV
value: production
- name: LOG_LEVEL
value: error
- name: DB_HOST
value: prod-db-cluster.example.com
- name: REDIS_URL
value: redis://prod-redis-cluster:6379
- name: METRICS_ENABLED # 新增
value: "true"
- name: TRACING_ENABLED # 新增
value: "true"
- name: SECURITY_HEADERS # 新增
value: "true"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 60
periodSeconds: 30
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 10
periodSeconds: 5
securityContext:
runAsNonRoot: true
runAsUser: 1000
readOnlyRootFilesystem: true
frontend:
helm:
values:
labels:
environment: production
monitoring: enabled
team: frontend
env:
- name: API_URL
value: https://api.example.com
- name: ANALYTICS_ENABLED # 新增
value: "true"
- name: CDN_URL # 新增
value: https://cdn.example.com
patches:
- op: add
path: deployments.backend.helm.values.annotations
value:
prometheus.io/scrape: "true"
prometheus.io/port: "3000"
prometheus.io/path: "/metrics"
- op: add
path: deployments.frontend.helm.values.annotations
value:
prometheus.io/scrape: "true"
prometheus.io/port: "80"
prometheus.io/path: "/metrics"
- op: replace
path: deployments.backend.helm.values.resources.limits.memory
value: "3Gi"
- op: add
path: deployments.backend.helm.values.nodeSelector
value:
node-type: "compute-optimized"
- op: add
path: deployments.backend.helm.values.affinity
value:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- backend
topologyKey: kubernetes.io/hostname
執行 devspace print -p production --var ENV=production
透過 ENV 啟用 production profile
version: v2beta1
name: demo
images:
backend:
image: mycompany/backend
tags:
- latest
dockerfile: ./backend/Dockerfile
frontend:
image: mycompany/frontend
tags:
- latest
dockerfile: ./frontend/Dockerfile
deployments:
backend:
helm:
chart:
name: ./charts/backend
values:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- backend
topologyKey: kubernetes.io/hostname
weight: 100
annotations:
prometheus.io/path: /metrics
prometheus.io/port: "3000"
prometheus.io/scrape: "true"
env:
- name: NODE_ENV
value: production
- name: LOG_LEVEL
value: error
- name: DB_HOST
value: prod-db-cluster.example.com
- name: REDIS_URL
value: redis://prod-redis-cluster:6379
- name: METRICS_ENABLED
value: "true"
- name: TRACING_ENABLED
value: "true"
- name: SECURITY_HEADERS
value: "true"
image:
repository: mycompany/backend
tag: prod-latest
labels:
environment: production
monitoring: enabled
team: backend
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 60
periodSeconds: 30
nodeSelector:
node-type: compute-optimized
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 10
periodSeconds: 5
replicaCount: 5
resources:
limits:
cpu: 1000m
memory: 3Gi
requests:
cpu: 500m
memory: 1Gi
securityContext:
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
service:
port: 3000
type: LoadBalancer
frontend:
helm:
chart:
name: ./charts/frontend
values:
annotations:
prometheus.io/path: /metrics
prometheus.io/port: "80"
prometheus.io/scrape: "true"
env:
- name: API_URL
value: https://api.example.com
- name: ANALYTICS_ENABLED
value: "true"
- name: CDN_URL
value: https://cdn.example.com
image:
repository: mycompany/frontend
tag: prod-latest
labels:
environment: production
monitoring: enabled
team: frontend
replicaCount: 3
service:
port: 80
type: LoadBalancer
dev:
backend:
imageSelector: mycompany/backend:latest
sync:
- path: ./backend:/app
excludePaths:
- node_modules/
terminal:
command: ./devspace_start.sh
ports:
- port: 3000:3000
如果有多個 profile
能透過devspace list profiles
簡易的了解有什麼 profile 能啟用。
Profiles 真的是很香!以前切換環境需要手動修改,現在根據變數和環境自動啟用對應 Profile。
還能夠使用 devspace print
預覽最終配置,避免部署意外。最重要的是能夠從開發環境逐步驗證到營運環境。